.section .vectors,"ax"
.global _start
_start:
    @ 1. 安装异常向量表
    b reset
    ldr pc,=undefine_instruction
    ldr pc,=software_interrupt
    ldr pc,=prefetch_abort
    ldr pc,=data_abort
    ldr pc,=not_used
    ldr pc,=irq
    ldr pc,=fiq

.text
    @ 2 reset异常
reset:
    cpsid i  @ 关闭IRQ中断
    @ 2.1 设置异常向量表的基地址,统一采用加载-修改-写回的方式修改寄存器
    @ 2.1.1 设置异常向量表基地址选择位为0(SCTLR bit[13])
    mrc p15,0,r0,c1,c0,0  @系统控制寄存器 (SCTLR) 的值读取到 r0 寄存器中
    bic r0, r0,#(0x1<<13)
    mcr p15,0,r0,c1,c0,0  @ 将r0寄存器里修改后的值写回到系统控制寄存器 (SCTLR)
    @ 2.1.2 
    ldr r0,=0x87800000    @ 设置异常向量表基地址为0x87800000
    mcr p15,0,r0,c12,c0,0 @ 将0x8780000写入到VBAR寄存器

    @2.2 设置系统控制寄存器以关闭MMU,I/D cache,分支预测,对齐检查等
    mrc p15,0,r0,c1,c0,0
    bic r0,r0,#(0x1<<0)      @ 关闭MMU
    bic r0,r0,#(0x1<<1)      @ 关闭内存对齐检查
    bic r0,r0,#(0x1<<2)      @ 关闭数据缓存
    bic r0,r0,#(0x1<<10)     @ 禁用SWP指令
    bic r0,r0,#(0x1<<11)     @ 禁用分支预测
    bic r0,r0,#(0x1<<12)     @ 关闭I Cache
    mcr p15,0,r0,c1,c0,0 
    
    @2.3 初始化IRQ模式的栈空间,栈空间大小为2M
    mrs r0,cpsr
    bic r0,r0,#0x1F 
    orr r0,r0,#0x12 @ CPSR寄存器IRQ模式取值为10010
    msr cpsr,r0
    ldr sp,=0x80600000 @ 栈空间类型为满递减栈

    @2.3 初始化系统模式栈空间,栈空间大小为1M
    @ 注：用户模式与系统模式共用同一套寄存器,因此栈顶指针寄存器相同
    mrs r0,cpsr
    bic r0,r0,#0x1F 
    orr r0,r0,#0x1F @ CPSR寄存器SYS模式取值为11111
    msr cpsr,r0
    ldr sp,=0x80400000

    @2.4 初始化FIQ模式的栈空间,栈空间大小为1M
    mrs r0,cpsr
    bic r0,r0,#0x1F 
    orr r0,r0,#0x11 @ CPSR寄存器FIQ模式取值为10001
    msr cpsr,r0
    ldr sp,=0x80300000
    
    @2.5 初始化SVC模式的栈空间,栈空间大小为2M
    mrs r0,cpsr
    bic r0,r0,#0x1F 
    orr r0,r0,#0x13 @ CPSR寄存器SVC模式取值为10011
    msr cpsr,r0
    ldr sp,=0x80200000

    cpsie i         @ 开启IRQ中断
    
    @ 2.6  清空bss段
    ldr r0,=__bss_start
    ldr r1,=__bss_end
    @ 将r2寄存器的值(0)写入r0所指向的空间,每写一个0,r0地址自动加1
    mov r2,#0 
    @ 将寄存器r2中的值存储到r0所指向的内存空间，然后R0的值递增
    @ IA:先存数据，后增地址
    @ IB:先增地址，后存数据
loop:
    stmia r0!,{r2}
    cmp r0,r1
    @ r0小于等于r1,继续循环
    blt loop
    @ 2.7. 跳入main函数
    b main

irq:
    @ 3.1 考虑指令三级流水线的影响,需要修正程序的返回地址
    sub lr,lr,#4
    @ 3.2 保存上下文(寄存器内容)到IRQ模式的栈空间
    stmfd sp!,{r0-r12,lr}
    @ 3.3 确保中断嵌套可以多级恢复,保存SPSR_irq寄存器
    mrs r0,spsr
    stmfd sp!,{r0}
    @ 3.4 读取GIC控制器的基地址,该地址保存于CBAR寄存器,CRn=c15,op1=4,CRm=c0,op2=0
    mrc p15,4,r1,c15,c0,0
    @ 3.5 获取中断ID,传递到上层方便根据中断ID调用具体的中断处理函数
    add r1,r1,#0x2000 @CPU端口基地址
    add r1,r1,#0x000c @IAR寄存器地址
    ldr r0,[r1]
    stmfd sp!,{r0,r1} 
    @3.6 IRQ模式默认会禁用新的IRQ中断,而SVC模式允许重新启用IRQ,从而支持中断嵌套
    cps #0x13 @切换到SVC模式
    stmfd sp!,{lr} 
    @3.7 执行中断处理函数
    @ C函数的参数默认按照r0,r1,r2,r3顺序传递,若只有一个参数,通过r0传递
    ldr r2,=System_IRQHandler 
    blx r2
    @3.8 中断处理函数执行结束,恢复中断处理前的返回地址
    ldmfd sp!,{lr}
    @3.9 返回IRQ模式
    cps #0x12
    @3.10 写EOIR寄存器以结束中断
    ldmfd sp!,{r0,r1}
    str r0,[r1,#0x10]
    @3.11 恢复程序状态
    ldmfd sp!,{r0}
    msr spsr,r0
    @3.12 恢复上下文
    ldmfd sp!,{r0-r12,lr}
    @3.13 恢复中断未发生时的程序状态并自动恢复CPSR 
    movs pc,lr

undefine_instruction:
    b stop
software_interrupt:
    b stop
prefetch_abort:
    b stop
data_abort:
    b stop
not_used:
    b stop
fiq:
    b stop
stop:
    b stop
.end

